package com.heima.wemedia.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.article.feign.ApArticleFeign;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.contatns.MQConstants;
import com.heima.common.dtos.AppHttpCodeEnum;
import com.heima.common.dtos.ResponseResult;
import com.heima.common.exception.LeadNewsException;
import com.heima.common.minio.MinIOFileStorageService;
import com.heima.model.wemeida.dtos.ArticleDto;
import com.heima.model.wemeida.pojos.WmChannel;
import com.heima.model.wemeida.pojos.WmNews;
import com.heima.model.wemeida.pojos.WmSensitive;
import com.heima.model.wemeida.pojos.WmUser;
import com.heima.utils.common.BeanHelper;
import com.heima.utils.common.JsonUtils;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsTaskService;
import io.seata.spring.annotation.GlobalTransactional;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private MinIOFileStorageService storageService;
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private ApArticleFeign apArticleFeign;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;
    @Autowired
    private ITesseract tesseract; // COR识别对象
    @Autowired
    private WmNewsTaskService wmNewsTaskService;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    @Override
    @Async //底层原理：Spring在底层会该业务方法的执行创建新的线程来执行
    @GlobalTransactional // 添加分布式事务
    public void autoScanWmNews(Integer id) {
        //根据id查询自媒体文章
        WmNews wmNews = wmNewsMapper.selectById(id);

        if(wmNews==null || !wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
           throw new LeadNewsException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //抽取文章的图片内容
        List<byte[]> imageList = getImageFromNews(wmNews);

        //抽取文章的文本内容
        List<String> textList = getTextFromNews(wmNews,imageList);

        //进行自定义敏感词检测
        if(CollectionUtils.isNotEmpty(textList)){
            boolean flag = handleSensitive(textList,wmNews);
            if(!flag){
                return;
            }
        }

        //提交文本给阿里云内容检测，根据检测结果修改文章的状态
       /* if(CollectionUtils.isNotEmpty(textList)){
            try {
                Map resultMap = greenTextScan.greeTextScan(textList);
                boolean flag = handleScanResult(resultMap,wmNews);
                if(!flag){
                    return;//如果不通过检测，则停止流程
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("阿里云API调用失败："+e);
            }
        }

        //提交图片给阿里云内容检测，根据检测结果修改文章的状态
        if(CollectionUtils.isNotEmpty(imageList)){
            try {
                Map resultMap = greenImageScan.imageScan(imageList);
                boolean flag = handleScanResult(resultMap,wmNews);
                if(!flag){
                    return;//如果不通过检测，则停止流程
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("阿里云API调用失败："+e);
            }
        }*/

        //判断发布时间是否大于当前时间，代表定时发布，修改文章状态8
        if(wmNews.getPublishTime()!=null && wmNews.getPublishTime().after(new Date())){
            wmNews.setStatus(WmNews.Status.SUCCESS.getCode());
            wmNews.setReason("审核成功，待发布");
            wmNewsMapper.updateById(wmNews);

            //把该文章加入延迟队列中
            wmNewsTaskService.addTaskToList(wmNews);

            return;//注意：必须退出程序
        }

        //把自媒体文章导入到App文章库，修改文章状态为9，代表发表完成（代表立即发表）
        saveApArticle(wmNews);
    }

    /**
     * 自定义敏感词检测
     * @param textList
     * @param wmNews
     * @return
     */
    private boolean handleSensitive(List<String> textList, WmNews wmNews) {
        boolean flag = true;

        //查询所有敏感词
        List<WmSensitive> sensitives = wmSensitiveMapper.selectList(null);

        if(CollectionUtils.isNotEmpty(sensitives)){
            List<String> words = sensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());

            //构建DFA词库
            SensitiveWordUtil.initMap(words);

            //匹配词库，获取检测结果
            Map<String, Integer> result = SensitiveWordUtil.matchWords(textList.stream().collect(Collectors.joining("")));

            //处理检测结果
            if(result!=null && result.size()>0){
                //修改文件状态
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文章包含违规内容："+result.keySet());
                wmNewsMapper.updateById(wmNews);
                flag = false;
            }
        }

        return flag;
    }

    /**
     * 把自媒体文章导入到App文章库，完成文章发表行为
     * @param wmNews
     */
    public void saveApArticle(WmNews wmNews) {
        //拷贝数据
        ArticleDto dto = BeanHelper.copyProperties(wmNews,ArticleDto.class);

        //dto的id
        dto.setId(wmNews.getArticleId());

        //查询文章作者信息
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if(wmUser!=null){
            dto.setAuthorId(Long.valueOf(wmUser.getId()));
            dto.setAuthorName(wmUser.getName());
        }
        //查询文章的频道信息
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if(wmChannel!=null){
            dto.setChannelId(wmChannel.getId());
            dto.setChannelName(wmChannel.getName());
        }
        dto.setLayout(wmNews.getType());
        dto.setFlag((byte)0);
        dto.setLikes(0);
        dto.setViews(0);
        dto.setComment(0);
        dto.setCollection(0);

        //保存App文章库
        ResponseResult<Long> responseResult = apArticleFeign.saveApArticle(dto);

        //int i = 100/0;//模拟异常发生

        //完成发布后，修改文章状态为9
        if(responseResult.getCode().equals(200)){
            //获取保存后App文章的ID
            Long articleId = responseResult.getData();
            wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
            //修改发布的App文章的ID
            wmNews.setArticleId(articleId);
            wmNews.setReason("发表成功");
            wmNewsMapper.updateById(wmNews);
        }

        //发送消息，同步更新ES的索引
        kafkaTemplate.send(MQConstants.WM_NEW_UP_ES_TOPIC,responseResult.getData().toString());
    }

    /**
     * 处理检测结果
     * @param resultMap
     * @param wmNews
     */
    private boolean handleScanResult(Map resultMap, WmNews wmNews) {
        boolean flag = false;
        String suggestion = (String)resultMap.get("suggestion");
        if(StringUtils.isNotEmpty(suggestion)){
            if("pass".equals(suggestion)){
                flag = true;
            }
            if("block".equals(suggestion)){
                //修改文章状态
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("内容违规，请及时更新");
                wmNewsMapper.updateById(wmNews);
            }
            if("review".equals(suggestion)){
                //进入人工审查
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("待人工审查");
                wmNewsMapper.updateById(wmNews);
            }
        }
        return flag;
    }

    /**
     * 从文章中提取图片内容
     * @param wmNews
     * @return
     */
    private List<byte[]> getImageFromNews(WmNews wmNews) {
        List<byte[]> imageList = new ArrayList<>();

        //内容图片
        if(StringUtils.isNotEmpty(wmNews.getContent())){
            List<Map> list = JsonUtils.toList(wmNews.getContent(),Map.class);
            for(Map map:list){
                if(map.get("type").equals("image")){
                    String url = (String)map.get("value");
                    //从MinIO下载该图片内容
                    byte[] image = storageService.downLoadFile(url);
                    imageList.add(image);
                }
            }
        }

        //封面图片
        if(StringUtils.isNotEmpty(wmNews.getImages())){
            String[] split = wmNews.getImages().split(",");
            for(String url:split){
                //从MinIO下载该图片内容
                byte[] image = storageService.downLoadFile(url);
                imageList.add(image);
            }
        }

        return imageList;
    }

    /**
     * 从文章中提取文字内容
     * @param wmNews
     * @return
     */
    private List<String> getTextFromNews(WmNews wmNews,List<byte[]> imageList) {
        List<String> textList = new ArrayList<>();
        //标题
        if(StringUtils.isNotEmpty(wmNews.getTitle())){
            textList.add(wmNews.getTitle());
        }
        //内容
        if(StringUtils.isNotEmpty(wmNews.getContent())){
            List<Map> list = JsonUtils.toList(wmNews.getContent(),Map.class);
            for(Map map:list){
                if(map.get("type").equals("text")){
                    textList.add( (String)map.get("value") );
                }
            }
        }
        //标签
        if(StringUtils.isNotEmpty(wmNews.getLabels())){
            textList.add( wmNews.getLabels() );
        }

        //扫描所有图片的内容，识别其中的文字，并且放入textList中
        if(CollectionUtils.isNotEmpty(imageList)){
            for(byte[] image:imageList){
                try {
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(image);//将字节数组存入ByteArrayInputStream
                    BufferedImage bufferedImage = ImageIO.read(inputStream);//将字节输入流读取成图片

                    String result = tesseract.doOCR(bufferedImage);
                    if(StringUtils.isNotEmpty(result)){
                        textList.add(result);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("OCR识别识别："+e);
                }
            }
        }

        return textList;
    }
}
